Skip to content

fix(git-transport): support path: "." / "" so skills at the repo root are fully fetched - #1711

Merged
dyoshikawa merged 1 commit into
dyoshikawa:mainfrom
besdar:main
May 28, 2026
Merged

fix(git-transport): support path: "." / "" so skills at the repo root are fully fetched#1711
dyoshikawa merged 1 commit into
dyoshikawa:mainfrom
besdar:main

Conversation

@besdar

@besdar besdar commented May 26, 2026

Copy link
Copy Markdown
Contributor

rulesync install with transport: "git" only fetches root-level files when the configured path points at the repository root ("", ".", or "./"). For example, given:

{
  "source": "https://github.com/feature-sliced/skills",
  "path": ".",
  "skills": ["feature-sliced-design"],
  "transport": "git"
}

…only README.md ends up in .rulesync/skills/.curated/feature-sliced-design/, while the actual skill (feature-sliced-design/SKILL.md + the references/ folder) is silently dropped. No error is reported, which makes the failure mode quite confusing — install happily says “Fetched 1 skill(s)”.

Root cause

fetchSkillFiles in src/lib/git-client.ts runs:

git clone --depth 1 --branch <ref> --no-checkout --filter=blob:none -- <url> <tmpDir>
git -C <tmpDir> sparse-checkout set -- <skillsPath>
git -C <tmpDir> checkout

git sparse-checkout in cone mode (the default since git 2.27) treats . / "" / ./ as “top-level files only — exclude every subdirectory”. So after checkout, the working tree contains only the files directly at the repo root, and the subsequent walkDirectory(<tmpDir>/.) only finds those root files.

The downstream groupRemoteFilesBySkillRoot fallback at src/cli/commands/install/install-rulesync.ts (the “rootLevelFiles → singleSkillName” branch) then groups every root-level file under the requested skill name. That’s why the README is written as if it were the body of the skill.

This makes it impossible to install via transport: "git" from any repository that stores skills at the root (<repo>/<skill-name>/SKILL.md without a skills/ container directory). feature-sliced/skills is one such repository — the github transport works fine because it uses the REST listDirectory API which has no equivalent quirk.

Fix

In fetchSkillFiles, treat "", ".", and "./" as “the whole repo”:

  • Call git -C <tmpDir> sparse-checkout disable instead of sparse-checkout set -- ., so the full working tree is restored.
  • Use <tmpDir> itself as the skills root passed to walkDirectory, instead of <tmpDir>/..

For any other path value the behavior is unchanged.

Repro

Before the fix:

$ cat rulesync.jsonc
{
  "sources": [
    {
      "source": "https://github.com/feature-sliced/skills",
      "path": ".",
      "skills": ["feature-sliced-design"],
      "transport": "git"
    }
  ]
}

$ rulesync install
Installing skills from 1 source(s)...
Fetched 1 skill(s) from https://github.com/feature-sliced/skills: feature-sliced-design
Installed 1 skill(s) from 1 source(s).

$ ls .rulesync/skills/.curated/feature-sliced-design
README.md          # ← only this file, no SKILL.md, no references/

After the fix (same config):

$ rulesync install
Installing skills from 1 source(s)...
Fetched 1 skill(s) from https://github.com/feature-sliced/skills: feature-sliced-design
Installed 1 skill(s) from 1 source(s).

$ ls .rulesync/skills/.curated/feature-sliced-design
SKILL.md  references/

$ ls .rulesync/skills/.curated/feature-sliced-design/references
asset-handling.md       excessive-entities.md      layer-structure.md
cross-import-patterns.md framework-integration.md   migration-guide.md
practical-examples.md

Changes

  • src/lib/git-client.ts — branch on skillsPath ∈ {"", ".", "./"}: call sparse-checkout disable and use the clone root directly as the skills root.
  • src/lib/git-client.test.ts — added two test cases:
    1. it.each(["", ".", "./"])("disables sparse-checkout when skillsPath is %j (repo root)", …) — asserts sparse-checkout disable is invoked and sparse-checkout set is not.
    2. "uses the clone directory itself as the skills root when skillsPath is the repo root" — asserts files are read from <tmpDir> directly.

Notes

  • The fallback path for repos like <repo>/SKILL.md (a single root-level skill, no per-skill subdirectory) already exists in groupRemoteFilesBySkillRoot and continues to work; this PR only makes the data reach that fallback when using transport: "git".
  • The github transport (transport: "github", the default) is unaffected — it never had this issue because it lists directories via the REST API.

@dyoshikawa
dyoshikawa merged commit 77c5f50 into dyoshikawa:main May 28, 2026
10 of 17 checks passed
@dyoshikawa

Copy link
Copy Markdown
Owner

@besdar Thank you!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants